﻿/*
	Copyright 2010 Intuitive Solutions

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

using System;
using Metamorph.Core.Bpm;
using Metamorph.Core.Smm;
using Metamorph.Core.Bpm.Events;
using Metamorph.Core.Monitoring;
using System.Collections.Generic;
using Metamorph.Core.Bpm.Activities;

namespace Metamorph.Core
{
	public class MetamorphManager
	{
		private static MetamorphManager smmInstance;

		public event StateChangedDelegate StateChangedEvent;
		public delegate void StateChangedDelegate(IStateMachine smStateMachine, State sState);


		private Dictionary<string, StateMachineDefinition>	dStateMachineDef;
		private Dictionary<string, Subprocess>				dBusinessProcessDef;


		private Dictionary<string, IStateMachine>		dStateMachines;
		private Dictionary<string, IBusinessProcess>	dBusinessProcesses;
		private IActionDispatcher						adActionDispatcher;
		private IConditionDispatcher					cdConditionDispatcher;


		private EventManager							emEventManager;

		public EventManager EventManager
		{
			get
			{
				return emEventManager;
			}
			set
			{
				emEventManager = value;
			}
		}

		private MetamorphManager()
		{
			dStateMachines			= new Dictionary<string, IStateMachine>();
			dBusinessProcesses		= new Dictionary<string,IBusinessProcess>();

			adActionDispatcher		= Metamorph.Core.ActionDispatcher.Instance;
			cdConditionDispatcher	= Metamorph.Core.ConditionDispatcher.Instance;

			MonitoringManager.Instance.StartMonitoring();

			dBusinessProcessDef		= new Dictionary<string, Subprocess>();
			dStateMachineDef		= new Dictionary<string, StateMachineDefinition>();

			emEventManager			= EventManager.Instance;
		}

		public static MetamorphManager Instance
		{
			get
			{
				if (smmInstance == null)
				{
					smmInstance = new MetamorphManager();
				}

				return smmInstance;
			}
		}

		public Dictionary<string, IStateMachine> StateMachines
		{
			get
			{
				return dStateMachines;
			}
			set
			{
				dStateMachines = value;
			}
		}

		public IActionDispatcher ActionDispatcher
		{
			get
			{
				return adActionDispatcher;
			}
			set
			{
				adActionDispatcher = value;
			}
		}

		public IConditionDispatcher ConditionDispatcher
		{
			get
			{
				return cdConditionDispatcher;
			}
			set
			{
				cdConditionDispatcher = value;
			}
		}

		public IBamClient BamClient
		{
			get
			{
				return MonitoringManager.Instance.BamClient;
			}
		}

		public void NotifyChange(IStateMachine smStateMachine, State sState)
		{
			if (StateChangedEvent != null)
			{
				StateChangedEvent(smStateMachine, sState);
			}
		}

		/// <summary>
		/// Gets an existing state machine instance.
		/// </summary>
		/// <param name="sStateMachineId"></param>
		/// <returns>The identifier of the state machine's instance.</returns>
		public IStateMachine GetStateMachine(string sStateMachineId)
		{
			IStateMachine smStateMachine = null;

			if (dStateMachines.ContainsKey(sStateMachineId))
			{
				smStateMachine = dStateMachines[sStateMachineId];
			}
			else
			{
				throw new Exception("The " + sStateMachineId + " state machine does not exist.");
			}

			return smStateMachine;
		}

		/// <summary>
		/// Creates a new or gets an existing state machine instance.
		/// </summary>
		/// <param name="sStateMachineName">The name of the state machine.</param>
		/// <param name="sStateMachineId">The identifier of the state machine's instance.</param>
		/// <returns></returns>
		public IStateMachine GetStateMachine(string sStateMachineName, string sStateMachineId)
		{
			IStateMachine smStateMachine = null;

			if (dStateMachines.ContainsKey(sStateMachineId))
			{
				smStateMachine = dStateMachines[sStateMachineId];

				if (!smStateMachine.Name.Equals(sStateMachineName))
				{
					throw new Exception("The " + sStateMachineId + " state machine is not of type: " + sStateMachineName + ".");
				}
			}
			else
			{
				if (!dStateMachineDef.ContainsKey(sStateMachineName))
				{
					StateMachineProvider smpStateMachineProvider = new StateMachineProvider();

					smpStateMachineProvider.Load(sStateMachineName);

					if (smpStateMachineProvider.StateMachine == null)
					{
						throw new Exception("The " + sStateMachineName + " state machine was not found, please check the name.");
					}

					dStateMachineDef.Add(sStateMachineName, smpStateMachineProvider.StateMachine);
				}

				smStateMachine = new StateMachine(dStateMachineDef[sStateMachineName], sStateMachineId);

				dStateMachines.Add(sStateMachineId, smStateMachine);
			}

			return smStateMachine;
		}

		/// <summary>
		/// Gets an existing business process instance.
		/// </summary>
		/// <param name="sBusinessProcessId"></param>
		/// <returns>The identifier of the business process's instance.</returns>
		public IBusinessProcess GetBusinessProcess(string sBusinessProcessId)
		{
			IBusinessProcess bpBusinessProcess = null;

			if (dBusinessProcesses.ContainsKey(sBusinessProcessId))
			{
				bpBusinessProcess = dBusinessProcesses[sBusinessProcessId];
			}
			else
			{
				throw new Exception("The " + sBusinessProcessId + " business process does not exist.");
			}

			return bpBusinessProcess;
		}

		/// <summary>
		/// Creates a new or gets an existing business process instance.
		/// </summary>
		/// <param name="sBusinessProcessName">The name of the business process.</param>
		/// <param name="sBusinessProcessId">The identifier of the business process's instance.</param>
		/// <returns></returns>
		public IBusinessProcess GetBusinessProcess(string sBusinessProcessName, string sBusinessProcessId)
		{
			IBusinessProcess bpBusinessProcess = null;

			if (dBusinessProcesses.ContainsKey(sBusinessProcessId))
			{
				bpBusinessProcess = dBusinessProcesses[sBusinessProcessId];

				if (!bpBusinessProcess.Name.Equals(sBusinessProcessName))
				{
					throw new Exception("The " + sBusinessProcessId + " business process is not of type: " + sBusinessProcessName + ".");
				}
			}
			else
			{
				if (!dBusinessProcessDef.ContainsKey(sBusinessProcessName))
				{
					BusinessProcessProvider bppBusinessProcessProvider = new BusinessProcessProvider();

					bppBusinessProcessProvider.Load(sBusinessProcessName);

					if (bppBusinessProcessProvider.BusinessProcess == null)
					{
						throw new Exception("The " + sBusinessProcessName + " business process was not found, please check the name.");
					}

					dBusinessProcessDef.Add(sBusinessProcessName, bppBusinessProcessProvider.BusinessProcess);
				}

				bpBusinessProcess = new BusinessProcess(dBusinessProcessDef[sBusinessProcessName], sBusinessProcessId);

				dBusinessProcesses.Add(sBusinessProcessId, bpBusinessProcess);
			}

			return bpBusinessProcess;
		}

		/// <summary>
		/// Creates a new or gets an existing business process definition's instance.
		/// </summary>
		/// <param name="sBusinessProcessName">The name of the business process.</param>
		/// <returns></returns>
		public Subprocess GetBusinessProcessDefinition(string sBusinessProcessName)
		{
			if (!dBusinessProcessDef.ContainsKey(sBusinessProcessName))
			{
				BusinessProcessProvider bppBusinessProcessProvider = new BusinessProcessProvider();

				bppBusinessProcessProvider.Load(sBusinessProcessName);

				if (bppBusinessProcessProvider.BusinessProcess == null)
				{
					throw new Exception("The " + sBusinessProcessName + " business process was not found, please check the name.");
				}

				dBusinessProcessDef.Add(sBusinessProcessName, bppBusinessProcessProvider.BusinessProcess);
			}

			return dBusinessProcessDef[sBusinessProcessName];
		}
	}
}
